using RRX.Xml.Helper;
using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.JavaScript;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Xml;
using System.Xml.Linq;

namespace RRX.Xml.DemoCanvas
{
    /// <summary>
    /// Interaktionslogik für PageDemoCanvas.xaml
    /// </summary>
    public partial class PageDemoCanvas : Page
    {
        public PageDemoCanvas()
        {
            InitializeComponent();
        }


        //<----XX---- Canvas
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                Canvas.Children.Clear();

                Canvas drawCanvas = new Canvas();
                Canvas.Children.Add(drawCanvas);

                XElement root = XElement.Load(new StringReader(TextBox.Text));
                if (root == null)
                    return;

                foreach (XElement element in root.Elements())
                {
                    switch (element.Name.LocalName)
                    {
                        case "x.lin":
                            DrawLine(element, drawCanvas);
                            break;

                        case "x.lsp":
                            DrawLine(element, drawCanvas);
                            break;

                        case "x.ply":
                            DrawPolyline(element, drawCanvas);
                            break;

                        case "x.pol":
                            DrawPolygon(element, drawCanvas);
                            break;

                        case "x.rec":
                            DrawRectangle(element, drawCanvas);
                            break;

                        case "x.elp":
                            DrawEllipse(element, drawCanvas);
                            break;

                        case "x.ptf":
                            DrawPathFixed(element, drawCanvas);
                            break;

                        case "x.txb":
                            DrawTextbox(element, drawCanvas);
                            break;

                        case "x.cmb":
                            DrawCombined(element, drawCanvas);
                            break;

                        case "x.cmp":
                            DrawComponent(element, drawCanvas);
                            break;

                        default:
                            Debug.WriteLine($"Unbekanntes Element: {element.Name.LocalName}");
                            break;
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"Fehler beim Zeichnen: {ex.Message}");
            }
        }

        private void DrawLine(XElement element, Canvas canvas)
        {
            if (!TryGetCommonAttributes(element, out List<Point> points, out double thickness, out Brush stroke))
            {
                Debug.WriteLine("x.lin übersprungen.");
                return;
            }

            if (points.Count < 2)
            {
                Debug.WriteLine("x.lin übersprungen: zu wenig Punkte.");
                return;
            }

            Line line = new Line
            {
                X1 = points[0].X,
                Y1 = points[0].Y,
                X2 = points[1].X,
                Y2 = points[1].Y,
                StrokeThickness = thickness,
                Stroke = stroke
            };

            canvas.Children.Add(line);
            Debug.WriteLine("Added: x.lin");
        }

        private void DrawPolyline(XElement element, Canvas canvas)
        {
            if (!TryGetCommonAttributes(element, out List<Point> points, out double thickness, out Brush stroke))
            {
                Debug.WriteLine("x.ply übersprungen.");
                return;
            }

            if (points.Count < 2)
            {
                Debug.WriteLine("x.ply übersprungen: zu wenig Punkte.");
                return;
            }

            Polyline polyline = new Polyline
            {
                Points = new PointCollection(points),
                StrokeThickness = thickness,
                Stroke = stroke
            };

            canvas.Children.Add(polyline);
            Debug.WriteLine("Added: x.ply");
        }

        private void DrawPolygon(XElement element, Canvas canvas)
        {
            if (!TryGetCommonAttributes(element, out List<Point> points, out double thickness, out Brush stroke))
            {
                Debug.WriteLine("x.pol übersprungen.");
                return;
            }

            if (points.Count < 2)
            {
                Debug.WriteLine("x.pol übersprungen: zu wenig Punkte.");
                return;
            }

            Brush fill = GetBrushAttribute(element, "fil");

            Polygon polygon = new Polygon
            {
                Points = new PointCollection(points),
                StrokeThickness = thickness,
                Stroke = stroke,
                Fill = fill
            };

            canvas.Children.Add(polygon);
            Debug.WriteLine("Added: x.pol");
        }

        private void DrawRectangle(XElement element, Canvas canvas)
        {
            if (!TryGetCommonAttributes(element, out List<Point> points, out double thickness, out Brush stroke))
            {
                Debug.WriteLine("x.rec übersprungen.");
                return;
            }

            if (points.Count < 2)
            {
                Debug.WriteLine("x.rec übersprungen: zu wenig Punkte.");
                return;
            }

            Brush fill = GetBrushAttribute(element, "fil");
            double radiusX = GetDoubleAttribute(element, "rx");
            double radiusY = GetDoubleAttribute(element, "ry");

            Point p1 = points[0];
            Point p2 = points[1];

            double left = Math.Min(p1.X, p2.X);
            double top = Math.Min(p1.Y, p2.Y);
            double width = Math.Abs(p2.X - p1.X);
            double height = Math.Abs(p2.Y - p1.Y);

            Rectangle rectangle = new Rectangle
            {
                Width = width,
                Height = height,
                RadiusX = radiusX,
                RadiusY = radiusY,
                StrokeThickness = thickness,
                Stroke = stroke,
                Fill = fill
            };

            Canvas.SetLeft(rectangle, left);
            Canvas.SetTop(rectangle, top);

            canvas.Children.Add(rectangle);
            Debug.WriteLine("Added: x.rec");
        }

        private void DrawEllipse(XElement element, Canvas canvas)
        {
            if (!TryGetCommonAttributes(element, out List<Point> points, out double thickness, out Brush stroke))
            {
                Debug.WriteLine("x.elp übersprungen.");
                return;
            }

            if (points.Count < 2)
            {
                Debug.WriteLine("x.elp übersprungen: zu wenig Punkte.");
                return;
            }

            Brush fill = GetBrushAttribute(element, "fil");
            double radiusX = GetDoubleAttribute(element, "rx");
            double radiusY = GetDoubleAttribute(element, "ry");

            Point p1 = points[0];
            Point p2 = points[1];

            double left = Math.Min(p1.X, p2.X);
            double top = Math.Min(p1.Y, p2.Y);
            double width = Math.Abs(p2.X - p1.X);
            double height = Math.Abs(p2.Y - p1.Y);

            Ellipse ellipse = new Ellipse
            {
                Width = width,
                Height = height,
                StrokeThickness = thickness,
                Stroke = stroke,
                Fill = fill
            };

            Canvas.SetLeft(ellipse, left);
            Canvas.SetTop(ellipse, top);

            canvas.Children.Add(ellipse);
            Debug.WriteLine("Added: x.elp");
        }

        private void DrawPathFixed(XElement element, Canvas canvas)
        {
            if (!TryGetCommonAttributes(element, out List<Point> points, out double thickness, out Brush stroke))
            {
                Debug.WriteLine("x.ptf übersprungen.");
                return;
            }

            if (points.Count < 2)
            {
                Debug.WriteLine("x.ptf übersprungen: zu wenig Punkte.");
                return;
            }

            Brush fill = GetBrushAttribute(element, "fil");
            double opacity = GetDoubleAttribute(element, "op", 1.0);
            double sx = GetDoubleAttribute(element, "sx", 1.0);
            double sy = GetDoubleAttribute(element, "sy", 1.0);
            double sweep = GetDoubleAttribute(element, "swp", 0.0);

            // Hier musst du den Pfad-String aus dem XML holen.
            // Beispiel: <x.ptf dat="M10,10 L100,100" />
            string data = GetStringAttribute(element, "dat");

            if (string.IsNullOrWhiteSpace(data))
            {
                Debug.WriteLine("x.ptf übersprungen: keine Path-Daten.");
                return;
            }

            Geometry geometry;
            try
            {
                geometry = Geometry.Parse(data);
            }
            catch
            {
                Debug.WriteLine("x.ptf übersprungen: ungültige Geometry-Daten.");
                return;
            }

            System.Windows.Shapes.Path path = new System.Windows.Shapes.Path
            {
                Data = geometry,
                Stroke = stroke,
                Fill = fill,
                Opacity = opacity,
                StrokeThickness = thickness,
                Stretch = Stretch.Fill,
                StrokeLineJoin = PenLineJoin.Round,
                IsHitTestVisible = false
            };


            Point p1 = points[0];
            Point p2 = points[1];

            Rect rect = new Rect(p1, p2);

            ScaleTransform scaleTransform = new ScaleTransform
            {
                ScaleX = sx,
                ScaleY = sy
            };

            RotateTransform rotateTransform = new RotateTransform
            {
                Angle = sweep
            };

            TransformGroup transformGroup = new TransformGroup();
            transformGroup.Children.Add(scaleTransform);
            transformGroup.Children.Add(rotateTransform);

            path.LayoutTransform = transformGroup;

            Viewbox viewbox = new Viewbox
            {
                Child = path,
                Stretch = Stretch.Fill,
                Width = rect.Width,
                Height = rect.Height,
                Opacity = opacity,
                IsHitTestVisible = false,
                RenderTransformOrigin = new Point(0.5, 0.5),
                Margin = new Thickness(0)
            };

            Canvas.SetLeft(viewbox, rect.Left);
            Canvas.SetTop(viewbox, rect.Top);

            canvas.Children.Add(viewbox);
            Debug.WriteLine("Added: x.ptf");
        }

        private void DrawTextbox(XElement element, Canvas canvas)
        {
            if (!TryGetCommonAttributes(element, out List<Point> points, out double thickness, out Brush stroke))
            {
                Debug.WriteLine("x.txb übersprungen.");
                return;
            }

            if (points.Count < 2)
            {
                Debug.WriteLine("x.txb übersprungen: zu wenig Punkte.");
                return;
            }

            Brush fill = GetBrushAttribute(element, "fil");
            double opacity = GetDoubleAttribute(element, "opt", 1.0);
            double rx = GetDoubleAttribute(element, "rx");
            double ry = GetDoubleAttribute(element, "ry");
            string dash = GetStringAttribute(element, "dsh") ?? "0";

            double sweep = GetDoubleAttribute(element, "swp", 0.0);
            double sx = GetDoubleAttribute(element, "sx", 1.0);
            double sy = GetDoubleAttribute(element, "sy", 1.0);

            string text = GetStringAttribute(element, "txt") ?? "";
            string textlink = GetStringAttribute(element, "tln") ?? "Text";
            string linkvalue = GetStringAttribute(element, "lkv") ?? "";

            double fontSize = GetDoubleAttribute(element, "fsz", 8);
            Brush fontColor = GetBrushAttribute(element, "fcl");
            FontFamily fontFamily = new FontFamily(GetStringAttribute(element, "ffm") ?? "Arial");

            HorizontalAlignment fontHorizontal = GetHorizontalAlignmentAttribute(element, "fha");
            VerticalAlignment fontVertical = GetVerticalAlignmentAttribute(element, "fva");

            Point p1 = points[0];
            Point p2 = points[1];

            double left = Math.Min(p1.X, p2.X);
            double top = Math.Min(p1.Y, p2.Y);
            double width = Math.Abs(p2.X - p1.X);
            double height = Math.Abs(p2.Y - p1.Y);

            Grid gridblock = new Grid
            {
                Width = width,
                Height = height,
                Opacity = opacity,
                IsHitTestVisible = false
            };

            if (thickness > 0 || fill.ToString() != "#00FFFFFF")
            {
                Rectangle rect = new Rectangle
                {
                    Width = width,
                    Height = height,
                    RadiusX = rx,
                    RadiusY = ry,
                    Stroke = stroke,
                    Fill = fill,
                    StrokeThickness = thickness,
                    StrokeLineJoin = PenLineJoin.Round,
                    Opacity = opacity
                };


                gridblock.Children.Add(rect);
            }

            TextBlock block = new TextBlock();

            if (textlink == "Text")
            {
                block.Text = text;
            }
            else
            {
                block.Text = "---";
            }

            if (!string.IsNullOrWhiteSpace(block.Text))
            {
                if (fontVertical == VerticalAlignment.Stretch)
                {
                    fontVertical = VerticalAlignment.Center;
                }

                switch (fontHorizontal)
                {
                    case HorizontalAlignment.Left:
                        block.TextAlignment = TextAlignment.Left;
                        break;

                    case HorizontalAlignment.Right:
                        block.TextAlignment = TextAlignment.Right;
                        break;

                    case HorizontalAlignment.Center:
                    default:
                        block.TextAlignment = TextAlignment.Center;
                        break;
                }

                block.FontSize = fontSize;
                block.Foreground = fontColor;
                block.FontFamily = fontFamily;
                block.VerticalAlignment = fontVertical;
                block.HorizontalAlignment = fontHorizontal;
                block.Margin = new Thickness(thickness);
                block.Background = Brushes.Transparent;
                block.IsHitTestVisible = false;

                ScaleTransform scaleTransform = new ScaleTransform
                {
                    ScaleX = sx,
                    ScaleY = sy
                };

                RotateTransform rotateTransform = new RotateTransform
                {
                    Angle = sweep
                };

                TransformGroup transformGroup = new TransformGroup();
                transformGroup.Children.Add(scaleTransform);
                transformGroup.Children.Add(rotateTransform);

                block.LayoutTransform = transformGroup;

                gridblock.Children.Add(block);
            }


            Canvas.SetLeft(gridblock, left);
            Canvas.SetTop(gridblock, top);

            canvas.Children.Add(gridblock);
            Debug.WriteLine("Added: x.txb");
        }



        private void DrawComponent(XElement element, Canvas canvas)
        {
            if (!TryGetCommonAttributes(element, out List<Point> points))
            {
                Debug.WriteLine("x.cmp übersprungen.");
                return;
            }

            if (points.Count < 2)
            {
                Debug.WriteLine("x.cmp übersprungen: zu wenig Punkte.");
                return;
            }

            double sx = GetDoubleAttribute(element, "sx", 1.0);
            double sy = GetDoubleAttribute(element, "sy", 1.0);
            double sweep = GetDoubleAttribute(element, "swp", 0.0);

            var viewbox = new Viewbox();

            Rect rect = new Rect(points[0], points[1]);

            double left = rect.Left;
            double top = rect.Top;

            viewbox.Width = rect.Width;
            viewbox.Height = rect.Height;

            Canvas.SetLeft(viewbox, left);
            Canvas.SetTop(viewbox, top);

            Grid grid = new Grid
            {
                Width = rect.Width,
                Height = rect.Height
            };

            Canvas CanvasShort = new Canvas();
            XElement xShort = CompressionHelper.CompressedBase64ToXElement(element.Attribute("cmb").Value);

            DrawShort(xShort, CanvasShort);

            grid.Children.Add(CanvasShort);

            // Transform
            TransformGroup transformGroup = new TransformGroup();
            transformGroup.Children.Add(new ScaleTransform(sx, sy));
            transformGroup.Children.Add(new RotateTransform(sweep));

            grid.LayoutTransform = transformGroup;

            viewbox.Child = grid;

            var child = element.Elements().FirstOrDefault(e => e.Name.LocalName == "cmp");

            if (child != null)
            {
                string ky = (string?)child.Attribute("g.ky");
                string lb = (string?)child.Attribute("g.lb");

                if (!string.IsNullOrEmpty(ky) && !string.IsNullOrEmpty(lb))
                {
                    viewbox.ToolTip = $"{ky}\n{lb}";
                }
            }


            canvas.Children.Add(viewbox);

            Debug.WriteLine("Added: x.cmp");

        }
        private void DrawCombined(XElement element, Canvas canvas)
        {
            if (!TryGetCommonAttributes(element, out List<Point> points))
            {
                Debug.WriteLine("x.cmb übersprungen.");
                return;
            }

            if (points.Count < 2)
            {
                Debug.WriteLine("x.cmb übersprungen: zu wenig Punkte.");
                return;
            }

            double sx = GetDoubleAttribute(element, "sx", 1.0);
            double sy = GetDoubleAttribute(element, "sy", 1.0);
            double sweep = GetDoubleAttribute(element, "swp", 0.0);

            var viewbox = new Viewbox();

            Rect rect = new Rect(points[0], points[1]);

            double left = rect.Left;
            double top = rect.Top;

            viewbox.Width = rect.Width;
            viewbox.Height = rect.Height;

            Canvas.SetLeft(viewbox, left);
            Canvas.SetTop(viewbox, top);

            Grid grid = new Grid
            {
                Width = rect.Width,
                Height = rect.Height
            };

            Canvas CanvasShort = new Canvas();

            XElement xShort = CompressionHelper.CompressedBase64ToXElement(element.Attribute("cmb").Value);

            DrawShort(xShort, CanvasShort);

            grid.Children.Add(CanvasShort);

            // Transform
            TransformGroup transformGroup = new TransformGroup();
            transformGroup.Children.Add(new ScaleTransform(sx, sy));
            transformGroup.Children.Add(new RotateTransform(sweep));

            grid.LayoutTransform = transformGroup;

            viewbox.Child = grid;

            canvas.Children.Add(viewbox);

            Debug.WriteLine("Added: x.cmb");
        }

        private void DrawChildCombined(XElement element, Canvas canvas)
        {
            if (!TryGetCommonAttributes(element, out List<Point> points))
            {
                Debug.WriteLine("x.cmb übersprungen.");
                return;
            }

            if (points.Count < 2)
            {
                Debug.WriteLine("x.cmb übersprungen: zu wenig Punkte.");
                return;
            }

            double sx = GetDoubleAttribute(element, "sx", 1.0);
            double sy = GetDoubleAttribute(element, "sy", 1.0);
            double sweep = GetDoubleAttribute(element, "swp", 0.0);

            var viewbox = new Viewbox();

            Rect rect = new Rect(points[0], points[1]);

            double left = rect.Left;
            double top = rect.Top;

            viewbox.Width = rect.Width;
            viewbox.Height = rect.Height;

            Canvas.SetLeft(viewbox, left);
            Canvas.SetTop(viewbox, top);

            Grid grid = new Grid
            {
                Width = rect.Width,
                Height = rect.Height
            };

            Canvas CanvasShort = new Canvas();

            DrawShort(element, CanvasShort);

            grid.Children.Add(CanvasShort);

            // Transform
            TransformGroup transformGroup = new TransformGroup();
            transformGroup.Children.Add(new ScaleTransform(sx, sy));
            transformGroup.Children.Add(new RotateTransform(sweep));

            grid.LayoutTransform = transformGroup;

            viewbox.Child = grid;

            canvas.Children.Add(viewbox);

            Debug.WriteLine("Added: x.cmb");
        }

        private void DrawShort(XElement element, Canvas canvas)
        {

            Point dimensions = PointSerializer.DeserializeSingle(element.Attribute("dim").Value);

            Debug.WriteLine(element);


            foreach (XElement child in element.Elements())
            {
                switch (child.Name.LocalName)
                {
                    case "x.lin":
                        DrawLine(child, canvas);
                        break;

                    case "x.ply":
                        DrawPolyline(child, canvas);
                        break;

                    case "x.pol":
                        DrawPolygon(child, canvas);
                        break;

                    case "x.rec":
                        DrawRectangle(child, canvas);
                        break;

                    case "x.elp":
                        DrawEllipse(child, canvas);
                        break;

                    case "x.cmb":
                        DrawChildCombined(child, canvas);
                        break;

                    case "x.ptf":
                        DrawPathFixed(child, canvas);
                        break;

                    case "x.txb":
                        DrawTextbox(child, canvas);
                        break;

                    default:
                        Debug.WriteLine($"Unbekanntes Child: {child.Name.LocalName}");
                        break;
                }
            }
        }


        //<----XX---- Funktionen
        private bool TryGetCommonAttributes(XElement element, out List<Point> points)
        {
            points = null;
            XAttribute ptsAttr = element.Attribute("pts");

            if (ptsAttr == null)
            {
                Debug.WriteLine($"{element.Name.LocalName} übersprungen: Attribut fehlt.");
                return false;
            }

            points = PointSerializer.Deserialize(ptsAttr.Value);
            if (points == null)
            {
                Debug.WriteLine($"{element.Name.LocalName} übersprungen: Punkte ungültig.");
                return false;
            }


            return true;
        }
        private bool TryGetCommonAttributes(XElement element, out List<Point> points, out double thickness, out Brush stroke)
        {
            points = null;
            thickness = 0;
            stroke = null;

            XAttribute ptsAttr = element.Attribute("pts");
            XAttribute thkAttr = element.Attribute("thk");
            XAttribute strAttr = element.Attribute("str");

            if (ptsAttr == null || thkAttr == null || strAttr == null)
            {
                Debug.WriteLine($"{element.Name.LocalName} übersprungen: Attribut fehlt.");
                return false;
            }

            points = PointSerializer.Deserialize(ptsAttr.Value);
            if (points == null)
            {
                Debug.WriteLine($"{element.Name.LocalName} übersprungen: Punkte ungültig.");
                return false;
            }

            if (!double.TryParse(thkAttr.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out thickness))
            {
                Debug.WriteLine($"{element.Name.LocalName} übersprungen: thk ungültig.");
                return false;
            }

            stroke = BrushSerializer.Deserialize(strAttr.Value);
            return true;
        }

        private Brush GetBrushAttribute(XElement element, string attributeName)
        {
            XAttribute attr = element.Attribute(attributeName);

            if (attr == null || string.IsNullOrWhiteSpace(attr.Value))
                return Brushes.Transparent;

            return BrushSerializer.Deserialize(attr.Value);
        }

        private double GetDoubleAttribute(XElement element, string attributeName)
        {
            XAttribute attr = element.Attribute(attributeName);

            if (attr == null)
                return 0;

            return double.TryParse(attr.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out double value) ? value : 0;
        }

        private double GetDoubleAttribute(XElement element, string name, double defaultValue = 0)
        {
            XAttribute attr = element.Attribute(name);

            if (attr == null)
                return defaultValue;

            return double.TryParse(attr.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out double value) ? value: defaultValue;
        }

        private string GetStringAttribute(XElement element, string attributeName, string defaultValue = null)
        {
            XAttribute attr = element.Attribute(attributeName);

            if (attr == null)
                return defaultValue;

            string value = attr.Value;

            if (string.IsNullOrWhiteSpace(value))
                return defaultValue;

            return value;
        }

        private HorizontalAlignment GetHorizontalAlignmentAttribute(XElement element, string name)
        {
            return ((string?)element.Attribute(name) ?? "0") switch
            {
                "0" => HorizontalAlignment.Left,
                "1" => HorizontalAlignment.Center,
                "2" => HorizontalAlignment.Right,
                _ => HorizontalAlignment.Left
            };
        }

        private VerticalAlignment GetVerticalAlignmentAttribute(XElement element, string name)
        {
            return ((string?)element.Attribute(name) ?? "0") switch
            {
                "0" => VerticalAlignment.Top,
                "1" => VerticalAlignment.Center,
                "2" => VerticalAlignment.Bottom,
                "3" => VerticalAlignment.Stretch,
                _ => VerticalAlignment.Top
            };
        }

        //<----XX---- ZoomPan
        private readonly MatrixTransform transform = new MatrixTransform();
        private Point initialMousePosition;
        private float zoomFactor = 1.2f;

        private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
        {
            Point MousePosition = new Point();
            MousePosition = transform.Inverse.Transform(e.GetPosition(this));

            if (e.ChangedButton == MouseButton.Right || e.MiddleButton == MouseButtonState.Pressed)
            {
                initialMousePosition = MousePosition;
            }
        }
        private void Canvas_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            base.OnMouseWheel(e);

            if (Keyboard.Modifiers == ModifierKeys.Shift)
            {
                Point MousePosition = new Point();
                MousePosition = transform.Inverse.Transform(e.GetPosition(this));

                var translate = new TranslateTransform(e.Delta * 0.25, 0);
                transform.Matrix = translate.Value * transform.Matrix;

                foreach (UIElement child in Canvas.Children.OfType<Canvas>())
                {
                    child.RenderTransform = transform;
                }
            }
            else
            {
                ZoomPanCanvas_Zoom(e.GetPosition(this), e.Delta);
            }
        }
        private void Canvas_MouseMove(object sender, MouseEventArgs e)
        {
            base.OnMouseMove(e);


            Point MousePosition = new Point();
            MousePosition = transform.Inverse.Transform(e.GetPosition(this));

            if (e.RightButton == MouseButtonState.Pressed || e.MiddleButton == MouseButtonState.Pressed)
            {
                Vector delta = Point.Subtract(MousePosition, initialMousePosition);
                var translate = new TranslateTransform(delta.X, delta.Y);
                transform.Matrix = translate.Value * transform.Matrix;

                foreach (UIElement child in Canvas.Children.OfType<Canvas>())
                {
                    child.RenderTransform = transform;
                }
            }
        }

        private void ZoomPanCanvas_Zoom(Point point, double delta)
        {
            float scaleFactor = zoomFactor; // z. B. 1.05f
            double currentScale = transform.Matrix.M11;

            double scaleMax = 80;
            double scaleMin = 0.02;

            // Begrenzung prüfen
            if ((delta > 0 && currentScale > scaleMax) ||
                (delta < 0 && currentScale < scaleMin))
                return;

            // Zoomrichtung: bei negativem delta Faktor invertieren
            if (delta < 0)
            {
                scaleFactor = 1f / scaleFactor;
            }

            // Neue Matrix skalieren relativ zum Mauspunkt
            Matrix scaleMatrix = transform.Matrix;
            scaleMatrix.ScaleAt(scaleFactor, scaleFactor, point.X, point.Y);
            transform.Matrix = scaleMatrix;

            // Wende transform nur auf die Zeichenebene an – nicht auf alle Kinder!
            foreach (var child in Canvas.Children.OfType<UIElement>())
            {
                // Optional: nur bestimmte Elemente transformieren
                if (child is Canvas)
                {
                    child.RenderTransform = transform;
                }
            }
        }
    }
}
